<?php

date_default_timezone_set('Asia/Shanghai');
ini_set('memory_limit', '50M');

class websocket
{
    public $conn_map = array();
    public $server;
    public $reids = null;
    public $timer = array();
    static $config;
    static $tail_num;
    static $log_path;
    static $format;
    static $ctr_list;

    const PROLOGUE = 'Nginx 日志数据';

    public function __construct($config)
    {
        self::$config = $config;
        $this->init_config();
        $this->server->start();
    }

    private function init_config()
    {
        $config = self::$config;
        $this->server = new swoole_websocket_server($config['host'], $config['port']);
        $this->server->on('open', array($this, 'onOpen'));
        $this->server->on('message', array($this, 'onMessage'));
        $this->server->on('close', array($this, 'onClose'));
        $this->server->set($config['swoole']);
        self::$tail_num = $config['tail_num'];
        self::$log_path = $config['log_path'];
        self::$format = $config['date_format'];
        self::$ctr_list = $config['ctr_list'];
    }

    public function create_redis()
    {
        if (!empty($this->redis)) {
            return $this->redis;
        }
        $config = self::$config;
        try {
            $this->redis = new redis();
            $this->redis->pconnect($config['redis']['host'], $config['redis']['port']);
            !empty($config['redis']['auth']) && $this->redis->auth($config['redis']['auth']);
        } catch (Exception $e) {
            echo 'redis 连接失败 :' . $e->getMessage();
            return false;
        }

        return $this->redis;
    }

    public function reload_redis()
    {
        $this->redis = null;
        $this->create_redis();
    }

    public function onOpen(swoole_websocket_server $server, $request)
    {
        $this->conn_map[$request->fd] = $request->fd;
        $this->push_all($request->fd . ' 进入 ');
    }


    public function onMessage(swoole_websocket_server $server, $frame)
    {
        $fd = $frame->fd;
        $data = $frame->data;
        $this->push_one($fd, self::PROLOGUE);
        if ('clear' == $data) {
            $this->clear_timer($fd);
        } else if ('total' == $data) {
            empty($this->timer) && $this->swoole_timer_total_tick($fd);
        } else if ('each' == $data) {
            empty($this->timer) && $this->swoole_timer_each_tick($fd);
        } else if ('stop' == $data) {
            sleep(10);
        } else {
            $msg = '命令无效';
            $this->push_one($fd, $msg);
        }

    }

    public function clear_timer($fd)
    {
        if (!empty($this->timer[$fd])) {
            swoole_timer_clear($this->timer[$fd]);
            $this->timer = array();
        }
    }

    public function swoole_timer_total_tick($fd)
    {
        $this->timer[$fd] = swoole_timer_tick(1000, function () {

            $nginx_data = $this->get_nginx_data();
            $nginx_json = json_encode($nginx_data);

            $this->push_all($nginx_json);
        });
    }

    public function swoole_timer_each_tick($fd)
    {
        $this->timer[$fd] = swoole_timer_tick(1000, function () {

            $nginx_data = $this->get_multi_api_data();
            $nginx_json = json_encode($nginx_data);

            $this->push_all($nginx_json);
        });
    }

    public function get_nginx_data()
    {
        $time = time() - 5;
        $date = date(self::$format, $time);
        $tail_num = self::$tail_num;
        $log_path = self::$log_path;

        $shell = " tail -n $tail_num  $log_path| grep '$date' | awk '{print $7}' | wc -l ";
        exec($shell, $request_arr);

        return array(
            'status' => 1,
            'num' => array_sum($request_arr),
            'date' => date('Y-m-d H:i:s', $time)
        );
    }

    public function get_multi_api_data()
    {
        $time = time() - 5;
        $date = date(self::$format, $time);
        $tail_num = self::$tail_num;
        $log_path = self::$log_path;
        $ctr_name = implode('|', self::$ctr_list);

        $shell = " tail -n $tail_num  $log_path| grep '$date' | awk '{print $7}' | awk -F '/' '{print $2\"/\"$3}' |grep -E \"$ctr_name\" |sort -r |uniq -c |sort -n -r |head -n 10";

        exec($shell, $shell_arr);
        $ctr_data = array();
        if (!empty($shell_arr)) {

            foreach ($shell_arr as $value) {
                list($val, $key) = explode(' ', trim($value));
                $ctr_data[$key] = $val;
            }
        }

        return array(
            'status' => 2,
            'ctr_data' => $ctr_data,
            'date' => date('Y-m-d H:i:s', $time)
        );
    }

    public function push_one($fd, $msg)
    {
        $this->server->push($fd, $msg);
    }

    public function push_all($msg)
    {
        foreach ($this->conn_map as $fd) {
            $this->server->push($fd, $msg);
        }
    }

    public function onClose(swoole_websocket_server $server, $cur_fd)
    {
        unset($this->conn_map[$cur_fd]);

        if (!empty($this->timer[$cur_fd])) {
            swoole_timer_clear($this->timer[$cur_fd]);
            $this->timer = array();;
        }

        $this->push_all($cur_fd . '退出连接');
    }
}

$config = array(
    'host' => '0.0.0.0',
    'port' => 9555,
    'swoole' => array(
        'daemonize' => 1,
        'worker_num' => 1,
        'max_request' => 50,
    ),
    'redis' => array(
        'host' => '127.0.0.1',
        'port' => '6397',
        'auth' => null
    ),
    # tail -n 数量
    'tail_num' => 10000,
    # 日志地址
    'log_path' => '/data/logs/nginx_access.log',
    # 时间格式化
    'date_format' => 'd/M/Y:H:i:s',
    #'date_format' => 'd/M/Y',
    # 控制器列表
    'ctr_list' => array(
        'Make',
        'Show',
        'Topic',
        'Role',
        'Stuff',
        'MeStall',
        'Config'
    )
);

$websocket = new websocket($config);
